package com.wltt.product.frfront.utils.http5;

import org.apache.hc.client5.http.classic.methods.*;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import org.apache.hc.core5.net.URIBuilder;
import org.apache.hc.core5.util.Timeout;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.http.HttpStatus;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: yanggl
 * @Date: Created in 2022/7/11 11:23
 * @Description:
 */
public abstract class Http5Util {

    private static final Logger logger = LogManager.getLogger(Http5Util.class);

    private static RequestConfig REQUEST_CONFIG = RequestConfig.custom()
            .setConnectTimeout(Timeout.ofMinutes(5))
            .setConnectionRequestTimeout(Timeout.ofMinutes(5))
            .setResponseTimeout(Timeout.ofMinutes(5))
            .setRedirectsEnabled(true)
            .build();


    /**
     * 发送请求
     *
     * @param url    请求地址
     */
    public static HttpResult doCall(HttpMethodEnum method, String url) throws IOException, URISyntaxException, ParseException {
        return doCall(method, url, null, null, null, REQUEST_CONFIG);
    }

    /**
     * 发送请求
     *
     * @param url    请求地址
     * @param params 请求参数
     */
    public static HttpResult doCall(HttpMethodEnum method, String url, List<NameValuePair> params) throws IOException, URISyntaxException, ParseException {
        return doCall(method, url, params, null, null, REQUEST_CONFIG);
    }

    /**
     * 发送请求
     *
     * @param method    调用方法
     * @param url       请求地址
     * @param params    请求参数
     * @param headerMap 请求头
     */
    public static HttpResult doCall(HttpMethodEnum method, String url,
                                List<NameValuePair> params,
                                Map<String, String> headerMap) throws IOException, URISyntaxException, ParseException {
        return doCall(method, url, params, headerMap, null, REQUEST_CONFIG);
    }

    /**
     * 发送请求
     *
     * @param method    调用方法
     * @param url       请求地址
     * @param params    请求参数
     * @param headerMap 请求头
     * @param body      请求体
     */
    public static HttpResult doCall(HttpMethodEnum method, String url,
                                List<NameValuePair> params,
                                Map<String, String> headerMap,
                                String body) throws IOException, URISyntaxException, ParseException {
        if (null != body && !headerMap.containsKey("Content-Type") && !headerMap.containsKey("content-type")) {
            throw new RemoteException("发送body需要指定\"Content-Type\", 如需发送json,可使用doCallJson");
        }
        return doCall(method, url, params, headerMap, body, REQUEST_CONFIG);
    }

    /**
     * 发送请求
     *
     * @param url     请求地址
     * @param jsonBody json数据
     */
    public static HttpResult doCallJson(HttpMethodEnum method, String url, String jsonBody) throws IOException, URISyntaxException, ParseException {
        Map<String, String> headerMap = new HashMap<>();
        headerMap.put("Content-Type", "application/json;charset=utf8");
        return doCall(method, url, null, headerMap, jsonBody, REQUEST_CONFIG);
    }

    /**
     * 发送请求
     *
     * @param method        调用方法
     * @param url           请求地址
     * @param body          请求体
     * @param params        请求参数
     * @param headerMap     请求头
     * @param requestConfig 配置信息
     */
    private static HttpResult doCall(HttpMethodEnum method, String url,
                                 List<NameValuePair> params,
                                 Map<String, String> headerMap,
                                 String body,
                                 RequestConfig requestConfig) throws IOException, URISyntaxException, ParseException {
        try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
            URI uri;
            if (null == params || params.size() < 1) {
                uri = new URIBuilder(url.trim()).build();
            } else {
                uri = new URIBuilder(url.trim()).setParameters(params).build();
            }
            HttpUriRequestBase httpRequest = getRequest(method, uri);

            if (null != body) {
                httpRequest.setEntity(new StringEntity(body, Charset.forName("UTF-8")));
            }
            if (null != headerMap && headerMap.size() > 0) {
                headerMap.entrySet().stream().map(Http5Util::convertEntryToBasicHeader)
                        .filter(basicHeader -> !basicHeader.getName().equalsIgnoreCase("Content-Length"))
                        .forEach(httpRequest::setHeader);
            }

            httpRequest.setConfig(requestConfig);

            try (CloseableHttpResponse httpResponse = httpClient.execute(httpRequest)) {
                HttpResult httpResult = new HttpResult();

                // 响应码
                int httCode = httpResponse.getCode();
                if (httCode < HttpStatus.OK.value() || httCode > HttpStatus.MULTIPLE_CHOICES.value()) {
                    logger.error("http error url: {}, httCode: {}", url, httCode);
                }
                httpResult.setHttpCode(httCode);


                // 响应头
                Header[] resHeaders = httpResponse.getHeaders();
                if (null != resHeaders && resHeaders.length > 0) {
                    Map<String, String> resHeader = new HashMap<>();
                    for (Header header : resHeaders) {
                        resHeader.put(header.getName(), header.getValue());
                    }
                    httpResult.setHeaders(resHeader);
                }

                // 响应body
                if (HttpStatus.NO_CONTENT.value() != httCode && HttpStatus.CREATED.value() != httCode) {
                    String res = EntityUtils.toString(httpResponse.getEntity(), Charset.forName("UTF-8"));
                    httpResult.setBody(res);
                }

                return httpResult;
            }
        }
    }

    private static BasicHeader convertEntryToBasicHeader(Map.Entry<String, String> entry) {
        return new BasicHeader(entry.getKey(), entry.getValue());
    }

    /**
     * 获取请求
     *
     * @param method        调用方法
     * @param uri           请求地址
     */
    private static HttpUriRequestBase getRequest(HttpMethodEnum method, URI uri) {
        if (HttpMethodEnum.GET.getCode() == method.getCode()) {
            return new HttpGet(uri);
        } else if (HttpMethodEnum.HEAD.getCode() == method.getCode()) {
            return new HttpHead(uri);
        } else if (HttpMethodEnum.POST.getCode() == method.getCode()) {
            return new HttpPost(uri);
        } else if (HttpMethodEnum.PUT.getCode() == method.getCode()) {
            return new HttpPut(uri);
        } else if (HttpMethodEnum.PATCH.getCode() == method.getCode()) {
            return new HttpPatch(uri);
        } else if (HttpMethodEnum.DELETE.getCode() == method.getCode()) {
            return new HttpDelete(uri);
        } else if (HttpMethodEnum.OPTIONS.getCode() == method.getCode()) {
            return new HttpOptions(uri);
        } else if (HttpMethodEnum.TRACE.getCode() == method.getCode()) {
            return new HttpTrace(uri);
        }
        throw new RuntimeException("不支持的http方法");
    }

    public static void main(String[] args) throws IOException, URISyntaxException, ParseException {
        Map<String, String> headers = new HashMap<>();
        List<NameValuePair> params = new ArrayList<>();
        params.add(new BasicNameValuePair("id", "1"));
        HttpResult httpResult = Http5Util.doCall(HttpMethodEnum.get("GET"), "https://www.baidu.com", params, headers, null);
        System.out.println(httpResult);
    }
}
